/*
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++   
#
# 
# Copyright (c) 2015-2020, Kunlun BMC, ZD Technology (Beijing) Co., Ltd.. All   
# Rights Reserved.                                                               
# 
# You may not reproduce, distribute, publish, display, perform, modify, adapt,   
# transmit, broadcast, present, recite, release, license or otherwise exploit    
# any part of this publication in any form, by any means, without the prior      
# written permission of ZD Technology (Beijing) Co., Ltd..                       
#
#
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++   
*/

#pragma once

//#include <memory>
#include <optional>
#include <sdbusplus/server.hpp>
#include <sdeventplus/clock.hpp>
#include <sdeventplus/event.hpp>
#include <sdeventplus/utility/timer.hpp>
#include <string>
#include <vector>

#define DFP_BUSNAME_PREFIX "xyz.openbmc_project.failure_prediction"
#define DFP_DBUS_ROOT "/xyz/openbmc_project/disk_failure_prediction"

/** Sleep interval in microseconds. */
static constexpr auto default_interval = 20;

typedef enum{
    HGST,
    ST,
    TOSHIBA,
    MANUFACT_MAX,
}MANUFACTURERS;

struct svm_node
{
	int index;
	double value;
};

struct svm_problem
{
	int l;
	double *y;
	struct svm_node **x;
};

enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR };	/* svm_type */
enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */

struct svm_parameter
{
	int svm_type;
	int kernel_type;
	int degree;	/* for poly */
	double gamma;	/* for poly/rbf/sigmoid */
	double coef0;	/* for poly/sigmoid */

	/* these are for training only */
	double cache_size; /* in MB */
	double eps;	/* stopping criteria */
	double C;	/* for C_SVC, EPSILON_SVR and NU_SVR */
	int nr_weight;		/* for C_SVC */
	int *weight_label;	/* for C_SVC */
	double* weight;		/* for C_SVC */
	double nu;	/* for NU_SVC, ONE_CLASS, and NU_SVR */
	double p;	/* for EPSILON_SVR */
	int shrinking;	/* use the shrinking heuristics */
	int probability; /* do probability estimates */
};

//
// svm_model
//
struct svm_model
{
	struct svm_parameter param;	/* parameter */
	int nr_class;		/* number of classes, = 2 in regression/one class svm */
	int l;			/* total #SV */
	struct svm_node **SV;		/* SVs (SV[l]) */
	double **sv_coef;	/* coefficients for SVs in decision functions (sv_coef[k-1][l]) */
	double *rho;		/* constants in decision functions (rho[k*(k-1)/2]) */
	double *probA;		/* pariwise probability information */
	double *probB;
	double *prob_density_marks;	/* probability information for ONE_CLASS */
	int *sv_indices;        /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */

	/* for classification only */

	int *label;		/* label of each class (label[k]) */
	int *nSV;		/* number of SVs for each class (nSV[k]) */
				/* nSV[0] + nSV[1] + ... + nSV[k-1] = l */
	/* XXX */
	int free_sv;		/* 1 if svm_model is created by svm_load_model*/
				/* 0 if svm_model is created by svm_train */
};

/** @class MainLoop
 *  @brief hwmon-readd main application loop.
 */
class PredictionLoop
{
  public:
    ~PredictionLoop();

    /** @brief Constructor
     *
     *  @param[in] bus - sdbusplus bus client connection.
     *  @param[in] param - the path parameter provided
     *  @param[in] path - hwmon sysfs instance to manage
     *  @param[in] devPath - physical device sysfs path.
     *  @param[in] prefix - DBus busname prefix.
     *  @param[in] root - DBus sensors namespace root.
     *  @param[in] interval - Sleep interval of the timer in microseconds(us).
     *
     *  Any DBus objects are created relative to the DBus
     *  sensors namespace root.
     *
     *  At startup, the application will own a busname with
     *  the format <prefix>.hwmon<n>.
     */
    PredictionLoop(sdbusplus::bus::bus&& bus, const char* prefix, const char* root, 
		    uint64_t interval);

    /** @brief Setup polling timer in a sd event loop and attach to D-Bus
     *         event loop.
     */
    void run();

    /** @brief Stop polling timer event loop from another thread.
     *
     *  Typically only used by testcases.
     */
    void shutdown() noexcept;
    
  private:
    /** @brief Read disk SMART info and do the prediction */
    void read();

    /** @brief init(load) the disk prediction model */
    void init();

    /** @brief sdbusplus bus client connection. */
    sdbusplus::bus::bus _bus;
    /** @brief sdbusplus freedesktop.ObjectManager storage. */
    sdbusplus::server::manager::manager _manager;

    /** @brief DBus busname prefix. */
    const char* _prefix;
    /** @brief DBus sensors namespace root. */
    const char* _root;
    /** @brief Sleep interval in microseconds. */
    uint64_t _interval;
    struct svm_model* _model;

    /** @brief the Event Loop structure */
    sdeventplus::Event _event;
    /** @brief Read Timer */
    sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
};
